﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System;
using System.Threading;
using System.ComponentModel;
using BrainTechLLC.ThreadSafeObjects;
using System.Runtime.Serialization;
#if NO_SILVERLIGHT
using AltSerialize;
#endif

namespace BrainTechLLC
{    
    public static class Everything
    {                
        public static readonly string _FileName = "C:\\everything.dat";
        public static readonly ThreadSafeLookup<long, IAssociatedID> _everything = new ThreadSafeLookup<long, IAssociatedID>();
        private const string AllGameThingsFileName = "C:\\gamethings.dat";
        private const string SingleGameThingFilenamePrefix = "C:\\gamething_";
        public static readonly ThreadSafeCounterLong _uniqueIdCounter = new ThreadSafeCounterLong();
        public static event EventHandler<ObjectEventArgsNonRef<long>> UnknownThing;

        public static void SaveEverythingList()
        {
#if NO_SILVERLIGHT            
            byte[] bytes = _everything.Keys.SerializeToBinary().Compress();
            File.WriteAllBytes(_FileName, bytes); 
#endif
        }

        public static List<long> LoadEverythingList()
        {
#if NO_SILVERLIGHT
            if (File.Exists(_FileName))
            {
                Serializer s = new Serializer();
                byte[] bytes = File.ReadAllBytes(_FileName).Decompress();                
                return bytes.DeserializeFromBinary() as List<long>;
            }
#endif
            return new List<long>();
        }

        public static void LoadGameThing(long v)
        {
#if NO_SILVERLIGHT
            string fileName = SingleGameThingFilenamePrefix + v.ToString();
            if (File.Exists(fileName))
            {
                Serializer s = new Serializer();
                byte[] bytes = File.ReadAllBytes(fileName).Decompress();
                IAssociatedID gameThing = s.Deserialize(bytes) as IAssociatedID;// bytes.DeserializeFromCompressedBinary() as IAssociatedID;
                Register(gameThing);
            }
#endif
        }

        public static void LoadAllGameThings()
        {
            List<long> ids = LoadEverythingList();

#if NO_SILVERLIGHT
            string fileName = AllGameThingsFileName;
            if (File.Exists(fileName))
            {                
                byte[] bytes = File.ReadAllBytes(fileName);                

                List<IAssociatedID> gameThings = bytes.DeserializeFromCompressedBinary() as List<IAssociatedID>;
                
                foreach (IAssociatedID gameThing in gameThings)
                    Register(gameThing);
            }

            ids.ForEach((v) =>
            {
                ICanSaveAndRestore saveAndRestore = _everything[v] as ICanSaveAndRestore;
                if (saveAndRestore != null) { saveAndRestore.PerformPostRestoreSetup(); }
            });
#endif
        }

        public static void SaveAllGameThings()
        {
#if NO_SILVERLIGHT
            string fileName = AllGameThingsFileName;
            List<IAssociatedID> gameThings = _everything.AllItems;            

            byte[] b = gameThings.SerializeToCompressedBinary();
            File.WriteAllBytes(fileName, b); 
            
            _everything.ForEach((v) =>
            {
                ICanSaveAndRestore saveAndRestore = v.Value as ICanSaveAndRestore;
                // Use Save from the ICanSaveAndRestore interface
                if (saveAndRestore != null) saveAndRestore.Save(null);
            });
#endif
        }

        public static T Find<T>(long id) where T : class, IAssociatedID
        {
            var item = _everything[id] as T;

            if (item == null && UnknownThing != null)
            {
                UnknownThing(null, new ObjectEventArgsNonRef<long>(id));
            }

            return item;
        }

        public static List<T> Find<T>(this IAssociatedIDCollection idCollection) where T : class, IAssociatedID
        {
            List<long> ids = idCollection.AllIDs;
            return Find<T>(ids);
        }

        public static List<T> Find<T>(this IEnumerable<long> ids) where T : class, IAssociatedID
        {
            List<T> results = new List<T>();
            ids.ForEach(l => results.Add(Find<T>(l)));
            return results;
        }

        public static List<long> FindAllIdsOfType<T>() where T : class, IAssociatedID
        {
            List<long> results = new List<long>();
            List<IAssociatedID> allThings = _everything.AllItems;
            foreach (IAssociatedID thing in allThings)
            {
                if (typeof(T).IsAssignableFrom(thing.GetType()))
                    results.Add(thing.AssociatedID);
            }

            return results;
        }

        public static List<T> FindAllThingsOfType<T>() where T : class, IAssociatedID
        {
            List<T> results = new List<T>();
            List<IAssociatedID> allThings = _everything.AllItems;
            foreach (IAssociatedID thing in allThings)
            {
                if (typeof(T).IsAssignableFrom(thing.GetType()))
                    results.Add(thing as T);
            }

            return results;
        }

        public static List<T> FindAllThingsOfType<T>(Func<T, bool> match) where T : class, IAssociatedID
        {
            List<T> results = new List<T>();
            List<IAssociatedID> allThings = _everything.AllItems;
            foreach (IAssociatedID thing in allThings)
            {
                if (typeof(T).IsAssignableFrom(thing.GetType()))
                {
                    T item = thing as T;

                    if (match(item))
                        results.Add(item);
                }
            }

            return results;
        }

        public static List<T> FindAllThingsOfType<T>(params Type[] t) where T : class, IAssociatedID
        {
            List<T> results = new List<T>();
            List<IAssociatedID> allThings = _everything.AllItems;
            foreach (IAssociatedID thing in allThings)
            {
                for (int n = 0; n < t.Length; n++)
                {
                    if (t[n].IsAssignableFrom(thing.GetType()))
                    {
                        results.Add(thing as T);
                        break;
                    }
                }
            }

            return results;
        }

        public static void Register(IAssociatedID thing)
        {
            _everything.AddOrSet(thing.AssociatedID, thing);
        }

        public static long CreateUniqueId()
        {
            long l = _uniqueIdCounter.Increment();

            while (UsedIds.Taken(l))
                l = _uniqueIdCounter.Increment();

            UsedIds.Add(l);
            return l;
        }
    }

    public static class EverythingHelper
    {
        public static IAssociatedID Find(this long id)
        {
            return Everything.Find<IAssociatedID>(id);
        }

        public static T Find<T>(this long id) where T : class, IAssociatedID
        {
            return (T)Everything.Find<T>(id);
        }
    }
}
